﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Drawing;
using Noris.Tools.TraceVisualiser.TraceData;
using Noris.Tools.TraceVisualiser.Components;
using Noris.Tools.TraceVisualiser.BlockingVisualiser.Data;
using Noris.Tools.TraceVisualiser.BlockingVisualiser.Forms;
using Noris.Tools.TraceVisualiser.Support;

namespace Noris.Tools.TraceVisualiser.BlockingVisualiser.Components
{
    #region class LockInfoCreator : Statická třída, která vytváří grafické objekty pro zobrazování dat zámků.
    /// <summary>
    /// Statická třída, která vytváří grafické objekty pro zobrazování dat zámků.
    /// </summary>
	internal static class LockInfoCreator
    {
        #region Tvorba dat
        /// <summary>
        /// Vytvoří a vrátí ListView pro zadaný proces
        /// </summary>
        /// <param name="traceData">Data trace dokumentu</param>
        /// <param name="spidInfo">Info o SQL události</param>
        /// <returns></returns>
        internal static LockInfoList CreateListFromProcess(EventInfo eventInfo, SqlSpidInfo spidInfo)
		{
			// List init:
            LockInfoList list = new LockInfoList();
            list.MaximumSize = new System.Drawing.Size(320, 720);
            list.MinimumSize = new System.Drawing.Size(260, 360);
            list.ContentType = TraceConstants.LIST_CONTENT_PROCESS;
            list.ContentId = spidInfo.SpID;
            list.ActiveEdge = LockInfoList.EdgeType.Lower;
			list.Groups.Clear();
            list.Items.Clear();
            Dictionary<string, ListViewGroup> groups = new Dictionary<string, ListViewGroup>();
            
            // Collect supply info:
            TraceInfoSupplyArgs supplyArgs = new TraceInfoSupplyArgs(eventInfo, TraceConstants.TRACESUPPLY_INFO_DEADLOCK);
            supplyArgs.SupplyData.Add("SpID", spidInfo.SpID);
            supplyArgs.SupplyData.Add("SpIdNumber", spidInfo.SpIdNumber);
            supplyArgs.SupplyData.Add("DeadlockTime", spidInfo.Parent.DeadlockTime);
            supplyArgs.SupplyData.Add("MaxTime", spidInfo.Parent.MaxTime);
            supplyArgs.SupplyData.Add("LastTransactionStarted", spidInfo.LastTransactionStarted);
            supplyArgs.SupplyData.Add("IsWaiterToAnyResource", spidInfo.IsWaiterToAnyResource);
            supplyArgs.SupplyData.Add("IsVictim", spidInfo.IsVictim);
            TraceInfoSupplyArgs.SupplyInfoCollect(supplyArgs);
            foreach (var info in supplyArgs.ResultInfoList)
            {
                try
                {
                    ListViewGroup group = _GetGroup(list, groups, info.GroupKey, info.GroupHeader);
                    ListViewItem item = _CreateListItem(list, group, info);
                    list.Items.Add(item);
                }
                catch (Exception)
                { }
            }

            // SPID:
            list.Items.Add(_CreateListItem(list, groups, TraceConstants.PROC_GROUP_HEADER, new TraceInfoSupplyItem() { Name = "SPID", Value = spidInfo.SpID }));

            // Procesy (může jich být více), každý má svoji vlastní grupu:
            foreach (SqlProcessInfo sqlProcess in spidInfo.Processes.Values)
            {
                ListViewGroup group = _GetGroup(list, groups, TraceConstants.PROC_GROUP_PROCESS + "_" + sqlProcess.Id, "Id: " + sqlProcess.Id);

                list.Items.Add(_CreateListItem(list, group, new TraceInfoSupplyItem() { Name = "LockMode", Value = sqlProcess.LockMode }));
                list.Items.Add(_CreateListItem(list, group, new TraceInfoSupplyItem() { Name = "Status", Value = sqlProcess.Status }));
                list.Items.Add(_CreateListItem(list, group, new TraceInfoSupplyItem() { Name = "TranCount", Value = sqlProcess.TranCount }));
                list.Items.Add(_CreateListItem(list, group, new TraceInfoSupplyItem() { Name = "WaitResource", Value = sqlProcess.WaitResource }));

                if (sqlProcess.LastTransactionStarted.HasValue)
                    list.Items.Add(_CreateListItem(list, group, new TraceInfoSupplyItem() { Name = "TransStart", Value = sqlProcess.LastTransactionStarted.Value.ToString("HH:mm:ss.fff"), ToolTipText = "LastTransactionStarted = " + sqlProcess.LastTransactionStarted.Value.ToString("dd.MM.yyyy HH:mm:ss.fff") }));
                if (sqlProcess.LastBatchCompleted.HasValue)
                    list.Items.Add(_CreateListItem(list, group, new TraceInfoSupplyItem() { Name = "BatchCompl", Value = sqlProcess.LastBatchCompleted.Value.ToString("HH:mm:ss.fff"), ToolTipText = "LastBatchCompleted = " + sqlProcess.LastBatchCompleted.Value.ToString("dd.MM.yyyy HH:mm:ss.fff") }));
                if (sqlProcess.LastBatchStarted.HasValue)
                    list.Items.Add(_CreateListItem(list, group, new TraceInfoSupplyItem() { Name = "BatchStart", Value = sqlProcess.LastBatchStarted.Value.ToString("HH:mm:ss.fff"), ToolTipText = "LastBatchStarted = " + sqlProcess.LastBatchStarted.Value.ToString("dd.MM.yyyy HH:mm:ss.fff") }));

                foreach (SqlInfo exeStack in sqlProcess.ExecutionStackList)
                    list.Items.Add(_CreateListItem(list, group, new TraceInfoSupplyItem() { Name = "SQL cmd", Value = exeStack.Value, ToolTipText = exeStack.Value, BackColor = Color.PaleGreen }));

                if (!String.IsNullOrEmpty(sqlProcess.InputBuffer))
                    list.Items.Add(_CreateListItem(list, group, new TraceInfoSupplyItem() { Name = "InputBuffer", Value = sqlProcess.InputBuffer, ToolTipText = sqlProcess.InputBuffer, BackColor = Color.PaleGreen }));

            }

			return list;
		}
        /// <summary>
        /// Vytvoří a vrátí ListView pro zadaný resource
        /// </summary>
        /// <param name="traceData">Data trace dokumentu</param>
        /// <param name="resource">Info o SQL zdroji (tabulka, index, ...)</param>
        /// <returns></returns>
        internal static LockInfoList CreateListFromResource(MainInfo traceData, SqlResourceInfo resource)
        {
            string eol = Environment.NewLine;

            LockInfoList list = new LockInfoList();
            list.Columns[0].Width = 72;
            list.MaximumSize = new System.Drawing.Size(300, 420);
            list.MinimumSize = new System.Drawing.Size(240, 330);
            list.ContentType = TraceConstants.LIST_CONTENT_RESOURCE;
            list.ContentId = resource.Id;
            list.ActiveEdge = LockInfoList.EdgeType.Upper;
            list.Groups.Clear();
            list.Items.Clear();
            Dictionary<string, ListViewGroup> groups = new Dictionary<string, ListViewGroup>();

            ListViewGroup groupHeader = _GetGroup(list, groups, TraceConstants.PROC_GROUP_HEADER);
            ListViewGroup groupOwner = _GetGroup(list, groups, TraceConstants.PROC_GROUP_OWNER);
            ListViewGroup groupWaiter = _GetGroup(list, groups, TraceConstants.PROC_GROUP_WAITER);
            ListViewGroup groupOther = _GetGroup(list, groups, TraceConstants.PROC_GROUP_OTHER);

            list.Items.Add(_CreateListItem(list, groupHeader, new TraceInfoSupplyItem() { Name = "Resource", Value = resource.Name, BackColor = Color.LightBlue }));
            
            List<SqlNameValue> attributes = new List<SqlNameValue>(resource.AttributeList);
            AddItemFromAttribute(list, attributes, groupHeader, "ObjectName", "objectname", Color.LightCoral);
            AddItemFromAttribute(list, attributes, groupHeader, "IndexName", "indexname", Color.LightCoral);
            AddItemFromAttribute(list, attributes, groupHeader, "Mode", "mode", Color.Empty);
            AddItemFromAttribute(list, attributes, groupHeader, "ObjectId", "id", Color.Empty);
            AddItemFromAttribute(list, attributes, groupHeader, "AssocId", "associatedObjectId", Color.Empty);

            AddItemFromAttribute(list, attributes, groupOther, "FileId", "fileid", Color.Empty);
            AddItemFromAttribute(list, attributes, groupOther, "DbId", "dbid", Color.Empty);
            AddItemFromAttribute(list, attributes, groupOther, "PageId", "pageid", Color.Empty);

            // Zapíšeme zbývající atributy (ty, co byly vypsané dříve, se ze seznamu odebraly):
            foreach (SqlNameValue snv in attributes)
                list.Items.Add(_CreateListItem(list, groupOther, new TraceInfoSupplyItem() { Name = snv.Name, Value = snv.Value, ToolTipText = snv.Name + " = " + snv.Value }));

            // Vlastníci:
            foreach (RelationSpidResource relation in resource.Owners)
                list.Items.Add(_CreateListItem(list, groupOwner, new TraceInfoSupplyItem() { Name = "SPID " + relation.SpidInfo.SpID, Value = relation.ProcessInfo.Id }));

            // Čekatelé:
            foreach (RelationSpidResource relation in resource.Waiters)
                list.Items.Add(_CreateListItem(list, groupWaiter, new TraceInfoSupplyItem() { Name = "SPID " + relation.SpidInfo.SpID, Value = relation.ProcessInfo.Id }));

            return list;
        }
        /// <summary>
        /// Vytvoří a vrátí seznam vztahu za tento zdroj.
        /// Vyplňuje: IsOwner, SpidInfo, ResourceInfo.
        /// </summary>
        /// <param name="resource"></param>
        /// <returns></returns>
        internal static List<LockInfoRelation> CreateRelations(SqlResourceInfo resource)
        {
            List<LockInfoRelation> relations = new List<LockInfoRelation>();

            // Vlastníci:
            foreach (RelationSpidResource relation in resource.Owners)
            {
                LockInfoRelation lockRel = new LockInfoRelation();
                lockRel.IsOwner = true;
                lockRel.RelationSpidResource = relation;
                relations.Add(lockRel);
            }

            // Čekatelé:
            foreach (RelationSpidResource relation in resource.Waiters)
            {
                LockInfoRelation lockRel = new LockInfoRelation();
                lockRel.IsOwner = false;
                lockRel.RelationSpidResource = relation;
                relations.Add(lockRel);
            }
            return relations;
        }
        /// <summary>
        /// Vyhledá existující grupu, nebo vytvoří novou a přidá do listu, a vrátí ji.
        /// </summary>
        /// <param name="list"></param>
        /// <param name="groups"></param>
        /// <param name="key"></param>
        /// <param name="text"></param>
        /// <returns></returns>
        private static ListViewGroup _GetGroup(LockInfoList list, Dictionary<string, ListViewGroup> groups, string key)
        {
            return _GetGroup(list, groups, key, null);
        }
        /// <summary>
        /// Vyhledá existující grupu, nebo vytvoří novou a přidá do listu, a vrátí ji.
        /// </summary>
        /// <param name="list"></param>
        /// <param name="groups"></param>
        /// <param name="key"></param>
        /// <param name="text"></param>
        /// <returns></returns>
        private static ListViewGroup _GetGroup(LockInfoList list, Dictionary<string, ListViewGroup> groups, string key, string text)
        {
            if (String.IsNullOrEmpty(key)) return null;                // Nezadané jméno
            ListViewGroup group;
            if (!groups.TryGetValue(key, out group))
            {
                string header = _GetGroupHeader(key, text);
                group = new ListViewGroup() { Header = header, Name = key };
                list.Groups.Add(group);
                groups.Add(key, group);
            }
            return group;
        }
        /// <summary>
        /// Vrátí text názvu skupiny podle jejího klíče
        /// </summary>
        /// <param name="key"></param>
        /// <param name="text"></param>
        /// <returns></returns>
        private static string _GetGroupHeader(string key, string text)
        {
            if (!String.IsNullOrEmpty(text)) return text;
            if (String.IsNullOrEmpty(key)) return "";
            switch (key)
            {
                case TraceConstants.PROC_GROUP_HEADER: return "Obecné informace";
                case TraceConstants.PROC_GROUP_PROCESS: return "";
                case TraceConstants.PROC_GROUP_OWNER: return "Vlastníci";
                case TraceConstants.PROC_GROUP_WAITER: return "Čekatelé";
                case TraceConstants.PROC_GROUP_OTHER: return "Další informace";
            }
            return key;
        }
        /// <summary>
        /// Najde atribut v datech, a pokud je zadán tak jej vloží do listu.
        /// Odebere jej ze seznamu atributů.
        /// </summary>
        /// <param name="list"></param>
        /// <param name="attributes"></param>
        /// <param name="listGroup"></param>
        /// <param name="label"></param>
        /// <param name="attributeName"></param>
        /// <param name="backColor"></param>
        private static void AddItemFromAttribute(LockInfoList list, List<SqlNameValue> attributes, ListViewGroup group, string label, string attributeName, Color backColor)
        {
            int idx = attributes.FindIndex(i => i.Name == attributeName);
            if (idx < 0) return;
            string value = attributes[idx].Value;
            if (!String.IsNullOrEmpty(value))
            {
                list.Items.Add(_CreateListItem(list, group, new TraceInfoSupplyItem() { Name = label, Value = value, ToolTipText = label + " = " + value, BackColor = backColor }));
            }
            attributes.RemoveAt(idx);
        }
        /// <summary>
        /// Vytvoří řádek Listu na základě dat v parametrech.
        /// </summary>
        /// <param name="list"></param>
        /// <param name="groupName"></param>
        /// <param name="dataRow"></param>
        /// <returns></returns>
        private static ListViewItem _CreateListItem(LockInfoList list, Dictionary<string, ListViewGroup> groups, string groupKey, TraceInfoSupplyItem dataRow)
        {
            ListViewGroup group = _GetGroup(list, groups, groupKey);
            return _CreateListItem(list, group, dataRow);
        }
        /// <summary>
        /// Vytvoří řádek Listu na základě dat v parametrech.
        /// </summary>
        /// <param name="list"></param>
        /// <param name="groupName"></param>
        /// <param name="dataRow"></param>
        /// <returns></returns>
		private static ListViewItem _CreateListItem(LockInfoList list, ListViewGroup group, TraceInfoSupplyItem dataRow)
		{
			ListViewItem item = new ListViewItem();
            item.Group = group;
			item.Text = dataRow.Name;
			item.SubItems.Add(dataRow.Value);
            if (!String.IsNullOrEmpty(dataRow.ToolTipText))
    			item.ToolTipText = dataRow.ToolTipText;
            if (!String.IsNullOrEmpty(dataRow.ImageKey))
                item.ImageKey = dataRow.ImageKey;
            if (!dataRow.BackColor.IsEmpty)
                item.BackColor = dataRow.BackColor;
            if (!dataRow.FontColor.IsEmpty)
                item.ForeColor = dataRow.FontColor;
            if (dataRow.Font != null)
                item.Font = dataRow.Font;
            item.UseItemStyleForSubItems = true;
			return item;
		}
        #endregion
    }
    #endregion
}
